<template>
  <div class="file-upload">
    <div
      v-on="events"
      :class="{ 'is-dragover': drag && isDragOver, 'upload-area': drag }"
    >
      <slot v-if="isUploading" name="loading"> 正在上传 </slot>
      <slot
        v-else-if="lastFileData && lastFileData.loaded"
        name="uploaded"
        :uploadedData="lastFileData.data"
      >
        <template v-if="drag">再次点击上传</template>
        <template v-else><button>再次点击上传</button></template>
      </slot>
      <slot v-else name="default">
        <template v-if="drag">点击上传</template>
        <template v-else><button>点击上传</button></template>
      </slot>
    </div>
    <input
      type="file"
      :style="{ display: 'none' }"
      ref="fileInput"
      @change.stop="uploadFile"
      :accept="accept"
      :multiple="multiple"
      :with-credentials="withCredentials"
    />
    <ul class="upload-list" v-if="showUploadList">
      <li
        :class="`uploaded-file upload-${file.status}`"
        v-for="file in uploadFiles"
        :key="file.uid"
      >
        <span v-if="file.status === 'loading'" class="file-icon">
          <LoadingOutlined />{{ file.percentage && `${file.percentage}%` }}
        </span>
        <span v-else class="file-icon">
          <FileOutlined />
        </span>
        <span class="filename">{{ file.name }}</span>
        <span class="delete-icon" @click="removeFile(file.uid)">
          <DeleteOutlined />
        </span>
      </li>
    </ul>
  </div>
</template>
<script setup lang="ts">
//npm install axios
// npm install @types/axios --save-dev
/* eslint-env node */
// 通用上传组件
import {
  ref,
  defineProps,
  nextTick,
  reactive,
  computed,
  watch,
  toRaw,
  withDefaults,
  defineEmits,
  defineExpose,
} from "vue";
import axios from "axios";
import { v4 as uuidv4 } from "uuid";
import {
  DeleteOutlined,
  LoadingOutlined,
  FileOutlined,
} from "@ant-design/icons-vue";

import { Button as aButton } from "ant-design-vue";
import { last } from "lodash-es";
import { UploadResp } from "@/extraType";
import { CheckUpload, ChangeParam, UploadFile } from "./Uploader";


const props = withDefaults(
  defineProps<{
    action: string;
    beforeUpload?: CheckUpload;
    onSuccess?: (param: ChangeParam) => void;
    onError?: (param: ChangeParam) => void;
    onProgress?: (param: ChangeParam) => void;
    // onChange?: (param: ChangeParam) => void;
    headers?: { [key: string]: string };
    data?: { [key: string]: string };
    multiple?: boolean;
    accept?: string;
    withCredentials?: boolean;
    drag?: boolean;
    showUploadList?: boolean;
    fileMaxCount?: number;
  }>(),
  {
    withCredentials: false,
    multiple: false,
    accept: "",
    headers: () => ({ "Content-Type": "multipart/form-data" }),
    data: () => ({}),
    drag: false,
    showUploadList: true,
    fileMaxCount: 1,
  }
);

const emit = defineEmits(["file-uploaded", "file-uploaded-error"]);



const fileInput = ref<HTMLInputElement | null>(null);
// const fileStatus = ref<UploadStastus>("ready");
const uploadFiles = ref<UploadFile[]>([]);
// 仅用于标记当前操作文件,无响应性
let currentFile: UploadFile | undefined;
const isUploading = computed(() => {
  // console.log(JSON.stringify(uploadFiles.value));
  return uploadFiles.value.some((file) => file.status === "loading");
});

const lastFileData = computed(() => {
  const lastFile = last(uploadFiles.value);
  if (lastFile) {
    return {
      loaded: lastFile.status === "success",
      data: lastFile.resp,
    };
  } else {
    return false;
  }
});

const removeFile = (id: string) => {
  uploadFiles.value = uploadFiles.value.filter((file) => file.uid !== id);
};

// watch(
//   () => uploadFiles.value,
//   () => {
//       console.log("uploader change ", currentFile);
//       props.onChange &&
//         props.onChange({
//           file: currentFile,
//           fileList: uploadFiles.value,
//         });
//   }
// );

const triggerUpload = () => {
  if (fileInput.value) {
    fileInput.value.click();
  }
};

defineExpose({ triggerUpload });

const isImageFile = (file: File): boolean => {
  return file.type.startsWith("image/");
};

const postFile = (file: File) => {
  const formData = new FormData();
  formData.append("file", file);
  Object.keys(props.data).forEach((key) => {
    formData.append(key, props.data[key]);
  });
  const header = {
    ...props.headers,
  };
  const fileObj = reactive<UploadFile>({
    uid: uuidv4(),
    size: file.size,
    name: file.name,
    status: "loading",
    raw: file,
  });
  if (isImageFile(file)) {
    fileObj.imgUrl = URL.createObjectURL(file); // 创建 URL 对象
  }

  currentFile = toRaw(fileObj);
 
  if (uploadFiles.value.length >= props.fileMaxCount) {
    //将第一个移除,然后在放入最后一个
    uploadFiles.value.splice(0, 1);
    uploadFiles.value.push(fileObj);
  } else {
    uploadFiles.value.push(fileObj);
  }

  console.log("File status set to uploading");
  axios
    .post(
      props.action,
      { data: formData },
      {
        headers: header,
        onUploadProgress(e) {
          if (e.total && props.onProgress) {
            const processFile = uploadFiles.value.find(
              (file) => file.uid === fileObj.uid
            ) as UploadFile;
            processFile.percentage = (e.loaded / e.total) * 100;
            props.onProgress({
              file: processFile,
              fileList: uploadFiles.value,
            });
          }
        },
      }
    )
    .then((res) => {
      console.log("Upload success", res);
      fileObj.status = "success";
      fileObj.resp = res.data;
      if (props.onSuccess) {
        props.onSuccess({
          file: fileObj,
          fileList: uploadFiles.value,
        });
      }
      // emit('file-uploaded', res.data);
      emit("file-uploaded", {
        code: 100,
        data: {
          url: fileObj.imgUrl,
        },
      } as UploadResp);
    })
    .catch((err) => {
      console.log("Upload error", err);
      fileObj.status = "error";
      if (props.onError) {
        props.onError({
          file: fileObj,
          fileList: uploadFiles.value,
        });
      }
      emit("file-uploaded", { err });
    })
    .finally(() => {
      if (fileInput.value) {
        fileInput.value.value = "";
      }
    });
};

const postFiles = (files: FileList) => {
  if (!files) {
    return;
  }
  
  for (let i = 0; i < files.length; i++) {
    const file = files[i];
    //校验
    if (props.beforeUpload) {
      const result = props.beforeUpload(file);
      if (result && result instanceof Promise) {
        result
          .then((file) => {
            if (file instanceof File) {
              postFile(file);
            } else {
              throw new Error("beforeUpload return need file");
            }
          })
          .catch((error) => {
            console.log(error);
          });
      } else if (result === true) {
        postFile(file);
      }
    } else {
      postFile(file);
    }
  }
};

let events: { [key: string]: (e: any) => void } = {
  click: triggerUpload,
};

const uploadFile = (e: Event) => {
  const files = (e.target as HTMLInputElement).files;
  if (files) {
    postFiles(files);
  }
};

const isDragOver = ref(false);

const handleDrag = (e: DragEvent, over: boolean) => {
  //拖拽图的时候可能会打开,所以要关闭默认行为
  e.preventDefault();
  isDragOver.value = over;
};

const handleDrop = (e: DragEvent) => {
  e.preventDefault();
  isDragOver.value = false;
  if (e.dataTransfer && e.dataTransfer.files) {
    const files = e.dataTransfer.files;
    if (files) {
      postFiles(files);
    }
  }
};

if (props.drag) {
  events = {
    ...events,
    dragover: (e: DragEvent) => handleDrag(e, true),
    dragleave: (e: DragEvent) => handleDrag(e, false),
    drop: handleDrop,
  };
}
</script>

<style scoped lang="less">
.file-upload {
  .upload-area {
    padding: 20px;
    width: 360px;
    height: 180px;
    background: @drag-background;
    border-radius: 4px;
    cursor: pointer;
    text-align: center;
    border: 1px dashed @drag-border;
    &:hover {
      border: 1px dashed @drag-over;
    }
    img {
      height: 100px;
    }
  }
  .is-dragover {
    border: 2px dashed @drag-over;
    background: rgba(@drag-over, 0.2);
  }
}

.upload-list {
  margin: 0;
  padding: 0;
  list-style-type: none;
  li {
    transition: all 0.5s cubic-bezier(0.55, 0, 0.1, 1);
    font-size: 14px;
    line-height: 1.8;
    margin-top: 5px;
    box-sizing: border-box;
    border-radius: 4px;
    min-width: 4px;
    position: relative;
    &:first-child {
      margin-top: 10px;
    }
    .file-icon {
      svg {
        margin-right: 5px;
        color: rgba(0, 0, 0, 0.45);
      }
    }

    .filename {
      margin-left: 5px;
      margin-right: 40px;
    }
    &.upload-error {
      color: @upload-error;
      svg {
        color: @upload-error;
      }
    }
    &.upload-success {
      color: @upload-success;
      svg {
        color: @upload-success;
      }
    }
    &.upload-loading {
      color: @upload-loading;
      svg {
        color: @upload-loading;
      }
    }
  }
}
</style>
